#include <stdio.h>
#define min(a,b) ((a)<(b))?(a):(b)
#include <windows.h>

unsigned volatile str_max_len=0;
char volatile *str_max=0;


class CriticalSection{
public:
	CriticalSection(){
		InitializeCriticalSection(&cs);
	};
	~CriticalSection(){
		DeleteCriticalSection(&cs);
	};

	void Enter(){
		EnterCriticalSection(&cs);
	};
	void Leave(){
		LeaveCriticalSection(&cs);
	};
private:
	CRITICAL_SECTION cs;
} max_sync;


class counter{
public:
	counter():cnt(0){
		memset(&cs,0,sizeof(cs));
		InitializeCriticalSection(&cs);
	};

	~counter(){
		DeleteCriticalSection(&cs);
	};

	unsigned get(){
		unsigned ret=0;
		EnterCriticalSection(&cs);
		ret=cnt++;
		LeaveCriticalSection(&cs);
		return ret;
	};

private:
	unsigned cnt;
	CRITICAL_SECTION cs;
};

typedef struct _txt{
	char* s;
	unsigned len;
}txt;

typedef struct _thrd_param{
	counter* ctr;
	txt *T,*P;
}thrd_param;

	void subsearch(char*,unsigned,char*,unsigned,counter*);
DWORD WINAPI worker(void* param){
	//thrd_param*& tp=reinterpret_cast<thrd_param*>(param); //VC doesn't believe that casted is lvalue. Lets waste stack.
	thrd_param *const tp=reinterpret_cast<thrd_param*>(param);

	subsearch(tp->T->s,tp->T->len,tp->P->s,tp->P->len,tp->ctr);
	ExitThread(0);//MSDN Caution:wouldn't call destructors
}


int __stdcall new_maxstr(char* s/*ESI*/,unsigned len){
	s-=len+1;//now s is common string
	max_sync.Enter();
		if(len>str_max_len){//compare and change can make troubles
			str_max=s;
			str_max_len=len;
		}
	max_sync.Leave();
	return str_max_len;
}

void subsearch(char* T/*ext*/,unsigned Tl,char* P/*attern*/,unsigned Pl,counter *cnt){
	unsigned i=cnt->get();

	for(;i<Tl-str_max_len && Pl>str_max_len;i=cnt->get()){
		unsigned len=min(Tl-i,Pl);// str_max_len can't begin races. Not synchronize
		_asm{
		mov ECX,len
		mov EAX,T
		mov EBX,i
		lea ESI,[EAX+EBX]
		mov EDI,P
		mov EBX,str_max_len//EBX will be str_max_len
		mov EDX,ECX//EDX will be ECX val. before cmps
		dec EDX
equals:
		cld
		repe cmpsb
	jnz not_last//or last not equals
		inc EDX //mean it end of string and last characters equals. 
		inc ESI
	not_last:
		sub EDX,ECX
		cmp EBX,EDX
	jae not_equals
		push ECX
		push EDX
		push ESI
	call new_maxstr
		pop ECX
		cmp EAX,EBX//assume EBX,ESI,EDI were saved
	jbe not_equals
		cmp ECX,EAX
	jbe to_break
		mov EBX,EAX
not_equals:
		test ECX,ECX
	jz to_break
		cld
		repne cmpsb
		test ECX,ECX
	jz to_break
		mov EDX,ECX
	jmp equals
to_break:
#ifdef _DEBUG
		push Tl
		push T
		dec ESI //if last characters equals its OK
		push ESI
		call pr_assert
		push Pl
		push P
		push EDI
		call pr_assert
		jmp to_end
pr_assert://catches out of bounds
		mov EAX,[ESP+4]
		sub EAX,[ESP+8]
		sub [ESP+12],EAX
		jnc OK
		xor EAX,EAX
		mov [EAX],EBX//throw
OK:		ret 12
to_end:
#endif
		}
	}
}

void search(char **DNA,unsigned DNAlen[]){
	counter c1,c2;
	SYSTEM_INFO si;

	//memset(&si,0,sizeof(si); //not essential?
	GetSystemInfo(&si);

	if(si.dwNumberOfProcessors<=1){
	printf("better use single-thread version\n");
		subsearch(DNA[0],DNAlen[0],DNA[1],DNAlen[1],&c1);
		subsearch(DNA[1],DNAlen[1],DNA[0],DNAlen[0],&c2);
	}else{
		DWORD& thrd_num=si.dwNumberOfProcessors;
		HANDLE *const thrds=new HANDLE[thrd_num];
		txt s1,s2;
		thrd_param p1,p2;

		memset(thrds,0,thrd_num*sizeof(HANDLE));
		s1.s=*DNA;s1.len=*DNAlen;
		s2.s=DNA[1];s2.len=DNAlen[1];
		p1.ctr=&c1;p1.T=&s1;p1.P=&s2;
		p2.ctr=&c2;p2.T=&s2;p2.P=&s1;

		thrd_param *p=&p1;
		for(unsigned i=0;i<thrd_num;i++){
			if(i==thrd_num>>1)p=&p2;
			thrds[i]=CreateThread(NULL,1024,worker,p,0,NULL);
			if(0==thrds[i]){
				printf("cannot create thread:%u\n",GetLastError());
				for(unsigned j=0;j<i;j++)TerminateThread(thrds[j],0);
				delete[] thrds;
				return;
			}
		}
		DWORD rez=WaitForMultipleObjects(thrd_num,thrds,TRUE,INFINITE);
		delete[](thrds);
		if(WAIT_FAILED==rez){
			printf("wait failed%u\n",GetLastError());
			return;
		}
	}

	if(!str_max){
		printf("nothing found");
		return;
	}
	str_max[str_max_len]=0;
	printf("length:%u\tfound:\n%s\n",str_max_len,str_max);
}
